﻿' EventSink.cs
' compile with: /doc:EventSink.xml
' <copyright>Copyright (c) Microsoft Corporation. All rights reserved.
' </copyright>
' <summary>This file contains the implementation for the EventSink 
' class which handles events from Visio.</summary>

Imports System
Imports System.Diagnostics
Imports Microsoft.Office.Interop.Visio
Imports Visio = Microsoft.Office.Interop.Visio
Imports Microsoft.Office.Interop.VisOcx

Imports Microsoft.Office.Interop.Visio.tagVisUICmds

Imports System.Diagnostics.CodeAnalysis
Imports Microsoft.Practices.EnterpriseLibrary.Common
Imports Microsoft.Practices.EnterpriseLibrary.Data
Imports Microsoft.Practices.EnterpriseLibrary.ExceptionHandling


Imports visRackAppConnectivity.clsVisioSupport


Imports System.Collections.ObjectModel
Imports System.Runtime.InteropServices



''' <summary>VisioEventHandler is an event delegate used for raising 
''' events. Events are used to decouple this class from the 
''' NetworkPlanSampleForm class.</summary>
''' 
''' <param name="sender">The Visio object that raised the event</param>
''' <param name="e">The arguments associated with the event</param>
''' Suppress this warning because generic event handler doesn't work here. 
''' Generic event handler uses parameter "sender" to differentiate the handlers,
''' but "OnShapeAdd" and "OnShapeDelete" handlers both use the "sender" to pass
''' a Visio shape. 
<SuppressMessage("Microsoft.Design", "CA1003:UseGenericEventHandlerInstances")> _
Public Delegate Sub VisioEventHandler(ByVal sender As Object, ByVal e As EventArgs)

''' <summary>The EventSink class handles events from Visio
''' which are specified in the AddAdvise method.</summary>
<System.Runtime.InteropServices.ComVisible(True)> _
Public NotInheritable Class clsEventSink
    Implements IVisEventProc



#Region " Properties and Definitions "

    '// <summary>visEvtAdd is declared as a 2-byte value to avoid a run-time
    '// overflow error.</summary>
    Private Const visEvtAdd As Short = -32768

    Private Const eventSinkCaption As String = "Event Sink"
    Private Const tab As String = "\t"
    Private eventDescriptions As  _
    System.Collections.Specialized.StringDictionary


    Private Const dc_rackArgument As String = "/dc_rack"
    Private Const doubleClickCommand As String = "/cmd=4"

    ''' <summary>Visio.Application object.</summary>
    Private eventApplication As Global.Microsoft.Office.Interop.Visio.Application

    ' for debug purposes
    Private WithEvents visioApplication As  _
      Microsoft.Office.Interop.Visio.Application
    Private scopeID As Integer


    ''' <summary>Visio.Document object.</summary>
    Private eventDocument As Document

    ''' <summary>Three FIFO queues are used to store added and deleted
    ''' shape information while Visio events are being processed.</summary>
    Private shapeAddedQueue As System.Collections.Queue
    Private shapeDeletedQueue As System.Collections.Queue
    Private pageTurnedQueue As System.Collections.Queue


    ''' <summary>OnShapeAdd event is raised when a shape is
    ''' added to the drawing.</summary>
    Public Event OnShapeAdd As VisioEventHandler

    ''' <summary>OnShapeDelete event is raised when a shape is
    ''' deleted from the drawing.</summary>
    Public Event OnShapeDelete As VisioEventHandler

    ''' <summary>OnShapeDoubleClick event is raised when the user
    ''' double-clicks a shape from the sample office furniture stencil.
    ''' </summary>
    Public Event OnShapeDoubleClick As VisioEventHandler

    Private m_clsvisio As clsVisioSupport


    Private Shared _lastShape As Shape
    Public ReadOnly Property lastShape() As Shape
        Get
            lastShape = _lastShape
        End Get
    End Property

    Private Shared _lastEvent As Integer
    Public ReadOnly Property lastEvent() As Integer
        Get
            lastEvent = _lastEvent
        End Get
    End Property

    Private Shared _lastPage As Page
    Public Property lastPage() As Page
        Get
            Return _lastPage
        End Get
        Set(ByVal value As Page)
            _lastPage = value
        End Set
    End Property

    ' place to save application current scope information 
    Private Shared m_lngCurrScope
    Public Property lngCurrScope() As Int32
        Get
            lngCurrScope = m_lngCurrScope
        End Get
        Set(ByVal value As Int32)
            m_lngCurrScope = value
        End Set
    End Property
    '
    ' globals for event processing
    '
    Private m_strCurrScope As String
    Public Property strCurrScope() As String
        Get
            strCurrScope = m_strCurrScope
        End Get
        Set(ByVal value As String)
            m_strCurrScope = value
        End Set
    End Property

    ' used with drawracks class
    Private m_dictComponents As Dictionary(Of Integer, String)
    Public Property dictComponents() As Dictionary(Of Integer, String)
        Get
            If m_dictComponents Is Nothing Then
                m_dictComponents = New Dictionary(Of Integer, String)
            End If
            dictComponents = m_dictComponents
        End Get
        Set(ByVal value As Dictionary(Of Integer, String))
            m_dictComponents = value
        End Set
    End Property

    ' used with drawracks class
    Private m_dictLinks As Dictionary(Of Integer, String)
    Public Property dictLinks() As Dictionary(Of Integer, String)
        Get
            If m_dictLinks Is Nothing Then
                m_dictLinks = New Dictionary(Of Integer, String)
            End If
            dictLinks = m_dictLinks
        End Get
        Set(ByVal value As Dictionary(Of Integer, String))
            m_dictLinks = value
        End Set
    End Property


#End Region



    ''' <summary>The EventSink default constructor creates the queues that
    ''' will be used to hold added and deleted shapes for processing.</summary>
    Public Sub New()

        ' Create the added and deleted queues. They will be cleaned
        ' up when the EventSink is deleted.
        shapeAddedQueue = New System.Collections.Queue
        shapeDeletedQueue = New System.Collections.Queue
        pageTurnedQueue = New System.Collections.Queue

        'these are dictionaries for document pages with shapeid's being the keys, and shape names
        m_dictLinks = New Dictionary(Of Integer, String)
        m_dictComponents = New Dictionary(Of Integer, String)

        m_clsvisio = New clsVisioSupport


        initializeStrings()

    End Sub



#Region " visio event capturing "


    ''' <summary>AddAdvise method takes the Visio application
    ''' and document and adds events for this solution to
    ''' their event lists.</summary>
    ''' <param name="callingApplication">The Visio application that
    ''' the user will be working with.</param>
    ''' <param name="callingDocument">The Visio document that the user
    ''' will be working with.</param>
    <CLSCompliant(False)> _
    Public Sub AddAdvise _
        (ByVal callingApplication As Global.Microsoft.Office.Interop.Visio.Application, _
         ByVal callingDocument As Document)

        ' The calling application must exist.
        If callingApplication Is Nothing Then

            ' Throw a meaningful error.
            Throw New ArgumentNullException("callingApplication", "Visio Application cannot be NULL")
        End If

        ' The calling document must exist.
        If callingDocument Is Nothing Then

            ' Throw a meaningful error.
            Throw New ArgumentNullException("callingDocument", "Visio Document cannot be NUll")
        End If

        ' Save the application for setting the events.
        eventApplication = callingApplication
        ' for debug
        ' visioApplication = callingApplication

        ' Save the document for setting the events.
        eventDocument = callingDocument

        ' Add events of interest.
        setAddAdvise()

    End Sub

    ''' <summary>The IVisEventProc.VisEventProc function is called by
    ''' Visio when an event which has been added to an events list
    ''' collection has been raised. The events were added to the
    ''' event lists in the SetAddAdvise method.</summary>
    ''' <param name="eventCode">Event code of the event that raised.
    ''' </param>
    ''' <param name="source">Reference to the object containing the
    ''' event that raised. This can be either an application or a
    ''' document, since both had events added to their eventlist.
    ''' </param>
    ''' <param name="eventId">Unique identifier of the event that
    ''' raised. This parameter is ignored in this case.</param>
    ''' <param name="eventSequenceNumber">The sequence of the event
    ''' relative to events that have raised so far. This parameter
    ''' is ignored in this case.</param>
    ''' <param name="subject">Reference to the object to which the
    ''' event occurred. For shape delete and add events, for example,
    ''' the subject is the shape.</param>
    ''' <param name="moreInformation">Additional information about the event.
    ''' This parameter is ignored in this case.</param>
    ''' <returns>The return value is ignored unless the event is a
    ''' query event. Since this sample does not process query events
    ''' an empty object is returned.</returns>
    Private Function VisEventProc _
        (ByVal eventCode As Short, _
         ByVal source As Object, _
         ByVal eventId As Integer, _
         ByVal eventSequenceNumber As Integer, _
         ByVal subject As Object, _
         ByVal moreInformation As Object) _
         As Object Implements IVisEventProc.VisEventProc



        Dim message As String = ""
        Dim name As String = ""
        Dim eventInformation As String = ""
        Dim returnValue As Object = True

        Try



            Dim subjectApplication As Microsoft.Office.Interop.Visio.Application = Nothing
            Dim subjectDocument As Microsoft.Office.Interop.Visio.Document = Nothing
            Dim subjectPage As Microsoft.Office.Interop.Visio.Page = Nothing
            Dim subjectMaster As Microsoft.Office.Interop.Visio.Master = Nothing
            Dim subjectSelection As Microsoft.Office.Interop.Visio.Selection = Nothing
            Dim subjectShape As Microsoft.Office.Interop.Visio.Shape = Nothing
            Dim subjectCell As Microsoft.Office.Interop.Visio.Cell = Nothing
            Dim subjectConnects As Microsoft.Office.Interop.Visio.Connects = Nothing
            Dim subjectStyle As Microsoft.Office.Interop.Visio.Style = Nothing
            Dim subjectWindow As Microsoft.Office.Interop.Visio.Window = Nothing
            Dim subjectMouseEvent As Microsoft.Office.Interop.Visio.MouseEvent = Nothing
            Dim subjectKeyboardEvent As Microsoft.Office.Interop.Visio.KeyboardEvent = Nothing
            Dim subjectDataRecordset As Microsoft.Office.Interop.Visio.DataRecordset = Nothing
            Dim subjectDataRecordsetChangedEvent As Microsoft.Office.Interop.Visio.DataRecordsetChangedEvent = Nothing


            Dim eventProcApplication As Global.Microsoft.Office.Interop.Visio.IVApplication = TryCast(source, IVApplication)


            If eventSequenceNumber = lastEvent Then
                'double handling the event 
                GoTo ExitFunction
            End If


            Select Case eventCode

                ' 
                ' shape level events
                '
                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                        visEvtShape) + visEvtAdd

                    If (eventCode And CShort(VisEventCodes.visEvtShape)) > 0 Then
                        subjectShape = DirectCast(subject, Shape)
                    End If

                    ' Handle the add-shape event.
                    handleShapeAdd(subjectShape)
                    Exit Select

                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                        visEvtShape) + CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtDel)

                    If 0 < (eventCode And CShort(VisEventCodes.visEvtShape)) Then
                        subjectShape = DirectCast(subject, Shape)

                        ' Handle the delete-shape event.
                        handleShapeDelete(subjectShape)


                    End If

                    Exit Select

                    '
                    ' application level events
                    '
                Case CShort(VisEventCodes.visEvtApp) + CShort(VisEventCodes.visEvtMarker)

                    ' Handle this marker event.
                    If eventProcApplication Is Nothing Then GoTo ExitFunction
                    handleMarker(eventProcApplication)
                    Exit Select

                Case CShort(VisEventCodes.visEvtApp) + CShort(VisEventCodes.visEvtNonePending)

                    ' Handle the no-events-pending event.
                    handleNonePending()
                    Exit Select


                    '
                    ' page level events
                    '
                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                            visEvtPage) + visEvtAdd
                    ' page add event 
                    ' Subject object is a Page
                    subjectPage = CType(subject, Microsoft.Office.Interop. _
                        Visio.Page)

                    handlePageAdd(subjectPage)

                    Exit Select

                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                        visEvtPage) + CShort(VisEventCodes.visEvtDel)
                    ' page delete event 
                    ' Subject object is a Page
                    subjectPage = CType(subject, Microsoft.Office.Interop. _
                        Visio.Page)

                    handlePageDelete(subjectPage)

                    Exit Select

                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeQueryCancelPageDel)
                    ' before page delete event 
                    ' Subject object is a Page
                    subjectPage = CType(subject, Microsoft.Office.Interop. _
                        Visio.Page)

                    ' handleQueryPageDelete(subjectPage)

                    Exit Select

                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeCancelPageDel)
                    ' before page delete event 
                    ' Subject object is a Page
                    subjectPage = CType(subject, Microsoft.Office.Interop. _
                        Visio.Page)

                    'handleCancelPageDelete(subjectPage)

                    Exit Select



                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeBefWinPageTurn)
                    subjectWindow = CType(subject, Microsoft.Office. _
                        Interop.Visio.Window)
                    subjectApplication = subjectWindow.Application
                    subjectPage = subjectWindow.Page
                    name = subjectPage.Name.ToString

                    handlePageTurn(subjectPage, "exit")

                    Exit Select


                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeWinPageTurn)
                    subjectWindow = CType(subject, Microsoft.Office. _
                        Interop.Visio.Window)
                    subjectApplication = subjectWindow.Application
                    subjectPage = subjectWindow.Page
                    name = subjectPage.Name.ToString

                    handlePageTurn(subjectPage, "enter")

                    Exit Select

                    '
                    ' selection based events
                    '

                    ' Query Cancel Selection Delete
                Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                    visEvtCodeQueryCancelSelDel)

                    subjectSelection = CType(subject, Microsoft.Office. _
                        Interop.Visio.Selection)

                    returnValue = handleQuerySelectionDelete(subjectSelection)
                    GoTo ExitFunction

                Case Else

                    Exit Select
            End Select

            _lastEvent = eventSequenceNumber

            ' get a description for this event code
            message = getEventDescription(eventCode)

            ' append the name of the subject object
            If (name.Length > 0) Then
                message += ": " + name
            End If

            ' append event info when it is available
            If (Not subjectApplication Is Nothing) Then

                eventInformation = subjectApplication.EventInfo( _
                    Microsoft.Office.Interop.Visio.VisEventCodes. _
                    visEvtIdMostRecent)

                If (Not eventInformation Is Nothing) Then
                    message += tab + eventInformation
                End If
            End If

            ' append moreInformation when it is available
            If (Not moreInformation Is Nothing) Then
                message += tab + moreInformation.ToString()
            End If

            ' get the targetArgs string from the event object. targetArgs
            ' are added to the event object in the AddAdvise method
            Dim events As Microsoft.Office.Interop.Visio.EventList
            Dim thisEvent As Microsoft.Office.Interop.Visio.Event
            Dim sourceType As String
            Dim targetArgs As String

            sourceType = source.GetType().FullName
            If (sourceType = _
                "Microsoft.Office.Interop.Visio.ApplicationClass") Then

                events = CType(source, Microsoft.Office.Interop.Visio. _
                Application).EventList
            ElseIf (sourceType = _
                "Microsoft.Office.Interop.Visio.DocumentClass") Then

                events = CType(source, Microsoft.Office.Interop.Visio. _
                Document).EventList
            ElseIf (sourceType = _
                "Microsoft.Office.Interop.Visio.PageClass") Then

                events = CType(source, Microsoft.Office.Interop.Visio. _
                Page).EventList
            Else
                events = Nothing
            End If

            If (Not events Is Nothing) Then

                thisEvent = events.ItemFromID(eventId)
                targetArgs = thisEvent.TargetArgs

                ' append targetArgs when it is available
                If (targetArgs.Length > 0) Then
                    message += " " + targetArgs
                End If
            End If

            ' Write the event info to the output window
            ' System.Diagnostics.Debug.WriteLine(message)

            ' if this is a QueryCancel event then prompt the user
            returnValue = getQueryCancelResponse(eventCode, subject)


        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

ExitFunction:

        Return returnValue

    End Function

    ''' <summary>The SetAddAdvise method adds the events that need to
    ''' be responded to with solution-specific behavior to the Visio
    ''' objects. For this sample, the add and delete shape events are
    ''' handled so that the data in the grid can be kept consistent
    ''' with the drawing. Marker events are handled to respond to shape
    ''' double-click from shapes in the sample office furniture stencil.
    ''' The none-pending event is also handled so that the add and delete
    ''' processing will only be done when Visio is idle.</summary>
    ''' <remarks>An Exception is thrown on failure.</remarks>
    Private Sub setAddAdvise()

        Try


            ' The Sink and TargetArgs values aren't needed.
            Const sink As String = ""
            Const targetArgs As String = ""

            Dim applicationEvents As EventList = eventApplication.EventList
            Dim documentEvents As EventList = eventDocument.EventList


            '
            ' page events
            '

            ' Add the PageAdded event.
            documentEvents.AddAdvise _
                (CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtPage + visEvtAdd), _
                DirectCast(Me, IVisEventProc), _
                sink, targetArgs)

            ' Add the PageDeleted event.
            documentEvents.AddAdvise _
                (CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtPage + VisEventCodes.visEvtDel), _
                DirectCast(Me, IVisEventProc), _
                sink, targetArgs)

            ' add the query cancel page delete
            documentEvents.AddAdvise _
            (CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeQueryCancelPageDel), _
               DirectCast(Me, IVisEventProc), _
               sink, targetArgs)

            ' add the page delete canceled
            documentEvents.AddAdvise _
            (CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeCancelPageDel), _
               DirectCast(Me, IVisEventProc), _
               sink, targetArgs)

            ' add the page delete canceled
            documentEvents.AddAdvise _
            (CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeCancelPageDel), _
               DirectCast(Me, IVisEventProc), _
               sink, targetArgs)

            ' Add the WindowTurnToPage event.
            applicationEvents.AddAdvise( _
                CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeWinPageTurn), _
                 DirectCast(Me, IVisEventProc), _
                sink, targetArgs)

            ' Add the Window before Turn to Page event.
            ' capture page setup for page turns
            applicationEvents.AddAdvise( _
                CShort(Microsoft.Office.Interop.Visio.VisEventCodes.visEvtCodeBefWinPageTurn), _
                DirectCast(Me, IVisEventProc), _
                sink, targetArgs)


            '
            ' shape events
            '

            ' Add the shape-added event to the document. The new shape
            ' will be available for processing in the handler. The
            ' value for VisEventCodes.visEvtAdd cannot be
            ' automatically converted to a short type, so the
            ' unchecked function is used. This allows the addition to
            ' be done and returns a valid short value.
            documentEvents.AddAdvise _
                    ((CShort(visEvtAdd) + CShort(VisEventCodes.visEvtShape)), _
                     DirectCast(Me, IVisEventProc), _
                     sink, targetArgs)

            ' Add the before-shape-deleted event to the document. This 
            ' event will be raised when a shape is deleted from the
            ' document. The deleted shape will still be available for
            ' processing in the handler.
            documentEvents.AddAdvise _
                (CShort(VisEventCodes.visEvtDel) + CShort(VisEventCodes.visEvtShape), _
                 DirectCast(Me, IVisEventProc), sink, targetArgs)

            ' Add marker events to the application. This event
            ' will be raised when a user double-clicks a shape
            applicationEvents.AddAdvise _
                (CShort(VisEventCodes.visEvtApp) + CShort(VisEventCodes.visEvtMarker), _
                 DirectCast(Me, IVisEventProc), _
                 sink, targetArgs)

            ' Add the none-pending event to the application. This
            ' event will be raised when Visio is idle.
            applicationEvents.AddAdvise _
                (CShort(VisEventCodes.visEvtApp) + CShort(VisEventCodes.visEvtNonePending), _
                 DirectCast(Me, IVisEventProc), _
                sink, targetArgs)


            ' Add the QueryCancelSelectionDelete event.
            documentEvents.AddAdvise(CShort(Microsoft.Office.Interop.Visio. _
                VisEventCodes.visEvtCodeQueryCancelSelDel), _
                  DirectCast(Me, IVisEventProc), _
                sink, targetArgs)



        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub


#End Region

#Region " Event Filtering "


    ''' <summary>The handleNonePending method is called when all Visio
    ''' events have been processed. The queued shape adds and deletes
    ''' are processed during Visio's idle time.</summary>
    Private Sub handleNonePending()


        Dim tempShape As Object = Nothing

        Try

            '
            ' Process the added-shapes queue.
            '
            If shapeAddedQueue IsNot Nothing Then

                ' Raise an OnShapeAdd event for each shape in the queue.
                While 0 < shapeAddedQueue.Count

                    tempShape = shapeAddedQueue.Dequeue()
                    RaiseEvent OnShapeAdd(tempShape, New EventArgs())

                End While
            Else

                ' There are no event listeners so just empty the queue.
                shapeAddedQueue.Clear()
            End If

            '
            ' Process the deleted-shapes queue.
            '
            If shapeDeletedQueue IsNot Nothing Then

                ' Raise an OnShapeDelete event for each shape in the 
                ' queue.
                While 0 < shapeDeletedQueue.Count

                    ' pass in the shapeid of the deleted shape
                    tempShape = shapeDeletedQueue.Dequeue()

                    ' make it a csv string with pointers for the draw racks
                    ' tempShape = getDictionaryValue(tempShape)

                    ' pass it out of the event handler
                    RaiseEvent OnShapeDelete(tempShape, New EventArgs)

                End While
            Else

                ' There are no event listeners so just empty the queue.
                shapeDeletedQueue.Clear()
            End If

            '
            ' Process the turned page queue.
            '
            If pageTurnedQueue.Count = 1 Then

                Dim visPage As Visio.Page = pageTurnedQueue.Dequeue
                Dim visPageSheet As Visio.Shape = Nothing
                ' get a list of components and links
                initPageDictionaries(visPage)
                ' set up the sync page size
                setSyncPageSize(visPage)
                pageTurnedQueue.Clear()

            End If

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub

    ''' <summary>The handleShapeAdd method is called when a shape
    ''' is added. The shape exists, so the shape reference can be
    ''' queued for use later. The actual processing of the add will
    ''' be done when all Visio events have been processed.</summary>
    ''' <param name="addedShape">The shape being added.</param>
    Private Sub handleShapeAdd(ByRef addedShape As Shape)

        Try


            If addedShape Is Nothing Then Exit Sub

            Dim strMasterName As String = ""
            Try
                Dim mstrShape As Visio.Master = addedShape.Master
                If mstrShape IsNot Nothing Then
                    strMasterName = addedShape.Master.NameU.ToString
                End If
            Catch
            End Try


            ' if we find a master name with a period the fix it
            Dim intPos As Int16 = InStr(strMasterName, ".")
            If 0 < intPos Then
                strMasterName = Mid(strMasterName, 1, intPos - 1)
            End If

            ' if we are in a create drawing then get out
            If eventApplication.IsInScope(lngCurrScope) Then
                '   Debug.Print("shaped added during draw recordset")

                ' was this shape added because of a group command
            ElseIf eventApplication.IsInScope(visCmdObjectGroup) Then
                'Debug.Print("Shape added using grouping action. " _
                '    & "Master name of the shape - " _
                '    & strMasterName)

                ' is in scope with apply data graphic
            ElseIf eventApplication.IsInScope(visCmdApplyDataGraphic) Then
                'Debug.Print("Shape Added using Apply Data Graphic")

                ' IsInScope with visCmdUFEditPaste parameter returns
                ' True if the shape is added using EditPaste action.
            ElseIf eventApplication.IsInScope(visCmdUFEditPaste) Then
                'Debug.Print("Shape added using EditPaste action. " _
                '    & "Master Name of the Shape - """ _
                '    & strMasterName)

                ' IsInScope with visCmdEditPasteSpecial parameter
                ' returns True if the shape is added using
                ' EditPasteSpecial action.
            ElseIf eventApplication.IsInScope(visCmdEditPasteSpecial) Then
                'Debug.Print("Shape added using EditPasteSpecial " _
                '    & "action. Master name of the shape - " _
                '    & strMasterName)
            Else

                If lastShape Is Nothing Then
                    _lastShape = addedShape
                Else
                    ' make sure this is not a double interrupt
                    If lastShape IsNot addedShape Then
                        _lastShape = addedShape
                    Else
                        Exit Sub
                    End If
                End If
                shapeAddedQueue.Enqueue(addedShape)
            End If

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub

    ''' <summary>The handleShapeDelete method is called when a shape
    ''' is being deleted. The shape still exists, so the information
    ''' can be read from the shape for use later. The actual
    ''' processing of the delete will be done when all Visio events
    ''' have been processed.</summary>
    ''' <param name="deletedShape">The shape being deleted.</param>
    Private Sub handleShapeDelete(ByVal deletedShape As Shape)

        Dim tempShape As Object = deletedShape.ID
        ' we enqueued the shape data as an eventhandler
        ' now we take the shape out of the page dictionary
        ' brute force approach 
        Try
            m_dictComponents.Remove(deletedShape.ID)
        Catch ex As Exception
        End Try
        Try
            m_dictLinks.Remove(deletedShape.ID)
        Catch ex As Exception
        End Try
    End Sub

    ''' <summary>The handleMarker method is called when Visio raises
    ''' a marker event. When the user double-clicks on a shape from the
    ''' sample office furniture stencil the formula in the shape's 
    ''' EventDblClick cell will run the QueueMarkerEvent addon which will
    ''' raise a marker event.</summary>
    ''' <param name="visioApplication">The Visio application that raised
    ''' this event.</param>
    <SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")> _
    Private Sub handleMarker(ByVal visioApplication As IVApplication)

        Dim arguments As String
        Dim targetShape As Shape

        Try

            'If the arguments include /dc_rack /cmd=4
            'then get a reference to the shape and raise the
            'OnShapeDoubleClick event

            arguments = visioApplication.EventInfo(CShort(VisEventCodes.visEvtIdMostRecent))
            If arguments = Nothing Then Exit Sub
            arguments = arguments.ToLowerInvariant()

            ' If this marker event was caused by double-clicking a 
            ' shape from the dc_rack components then raise an OnShapeDoubleClick event
            If (arguments.IndexOf(dc_rackArgument, StringComparison.Ordinal) >= 0) _
                AndAlso (arguments.IndexOf(doubleClickCommand, StringComparison.Ordinal) >= 0) Then

                ' Get a reference to this shape
                targetShape = GetShapeFromArguments(visioApplication, arguments)

                ' Raise an OnShapeDoubleClick event for this shape.
                RaiseEvent OnShapeDoubleClick(targetShape, New EventArgs())

            End If


        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub

    '
    '
    '
    Private Sub handlePageTurn _
        (ByVal subjectPage As Microsoft.Office.Interop.Visio.Page, _
         ByVal operationString As String)

        Select Case operationString
            Case "enter"
                pageTurnedQueue.Enqueue(subjectPage)
                Exit Select
            Case "exit"
                getSyncPageSize(subjectPage)
                lastPage = subjectPage
                Exit Select
        End Select

    End Sub

    '
    '
    '
    Private Sub handlePageAdd _
        (ByVal addedPage As Microsoft.Office.Interop.Visio.Page)

        Dim visPageSheet As Visio.Shape = addedPage.PageSheet
        If visPageSheet.CellExists("prop.pagesync", False) = False Then
            m_clsvisio.ForceShapeData(visPageSheet, "PageSync", "", "attrPage")
        End If

    End Sub

    '
    '
    '
    Private Sub handlePageDelete _
        (ByVal deletedPage As Microsoft.Office.Interop.Visio.Page)

        Dim visPageTemp As Visio.Page = Nothing
        Dim visCell As Visio.Cell
        Dim visPageSheet As Visio.Shape = deletedPage.PageSheet
        ' make sure the page passed to us has the custom property
        If visPageSheet.CellExists("prop.pagesync", False) = True Then
            ' get the page
            Dim strValue As String = visPageSheet.Cells("prop.pagesync").ResultStr("")
            If strValue = "" Then Exit Sub
            ' go find the other end and clear it if it exists
            Dim activeDoc As Visio.Document = deletedPage.Document
            If Not (activeDoc.Pages(strValue) Is Nothing) Then
                visPageTemp = activeDoc.Pages(strValue)
                If Not (visPageTemp Is Nothing) Then
                    visPageSheet = visPageTemp.PageSheet
                    If visPageSheet.CellExists("prop.pagesync", False) = True Then
                        visCell = visPageSheet.Cells("prop.pagesync")
                        visCell.FormulaU = ""
                    End If
                End If
            End If
        End If

    End Sub


    '
    ' our default is to get rid of the shape 
    '
    Private Function handleQuerySelectionDelete(ByVal visSel As Visio.Selection) _
        As Boolean

        ' default is to delete the shape
        Dim blnReturn As Boolean = False

        Try

            Dim intCt As Integer = 0
            Dim selCt As Integer = visSel.Count
            Dim deletedShape As Visio.Shape = Nothing



            ' did they try to cut the shape?

            If eventApplication.IsInScope(1020) Then
                MsgBox("Attempted Shape Delete with Cut" & vbCrLf & "Not Allowed")
                blnReturn = True
                ' is it really a delete
            ElseIf eventApplication.IsInScope(1023) Then
                'MsgBox("Attempted Shape Delete with Delete " & vbCrLf & visShape.NameU)
                ' where we do our own shape delete handling
                ' if it returns true then the shape is not deleted
                ' one and only one shape selected
                If selCt = 1 Then
                    deletedShape = visSel.Item(1)
                    shapeDeletedQueue.Enqueue(deletedShape.ID)
                    blnReturn = False
                Else
                    Dim msgResult As MsgBoxResult
                    Dim strShapeCt As String = "Delete shapes (" & selCt.ToString & ") selected ?"
                    msgResult = MsgBox(strShapeCt, MsgBoxStyle.OkCancel, "Delete Multiple Shapes")
                    If msgResult = MsgBoxResult.Ok Then
                        For intCt = 1 To selCt
                            deletedShape = visSel(intCt)
                            shapeDeletedQueue.Enqueue(deletedShape.ID)
                        Next
                        blnReturn = False
                    Else
                        eventApplication.ActiveWindow.DeselectAll()
                        blnReturn = True
                    End If
                    GoTo ExitFunction
                End If
            Else
                MsgBox("Unknown Delete Code = " & eventApplication.CurrentScope)
                blnReturn = True
            End If


        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try


ExitFunction:
        Return blnReturn

    End Function

#End Region

#Region " Page Methods "


    ' page sync definitions
    Private Shared m_ps_zoom As Double
    Private Shared m_ps_left As Double
    Private Shared m_ps_top As Double
    Private Shared m_ps_width As Double
    Private Shared m_ps_height As Double
    Private Shared m_ps_pagea As String
    Private Shared m_ps_pageb As String


    Property pZoom() As Double
        Get
            pZoom = m_ps_zoom
        End Get
        Set(ByVal value As Double)
            m_ps_zoom = value
        End Set
    End Property
    Property pLeft() As Double
        Get
            pLeft = m_ps_left
        End Get
        Set(ByVal value As Double)
            m_ps_left = value
        End Set
    End Property
    Property pTop() As Double
        Get
            pTop = m_ps_top
        End Get
        Set(ByVal value As Double)
            m_ps_top = value
        End Set
    End Property
    Property pHeight() As Double
        Get
            pHeight = m_ps_height
        End Get
        Set(ByVal value As Double)
            m_ps_height = value
        End Set
    End Property
    Property pWidth() As Double
        Get
            pWidth = m_ps_width
        End Get
        Set(ByVal value As Double)
            m_ps_width = value
        End Set
    End Property
    Property pPageA() As String
        Get
            pPageA = m_ps_pagea
        End Get
        Set(ByVal value As String)
            m_ps_pagea = value
        End Set
    End Property
    Property pPageB() As String
        Get
            pPageB = m_ps_pageb
        End Get
        Set(ByVal value As String)
            m_ps_pageb = value
        End Set
    End Property

    ' 
    ' build two shapeid dictionaries for the passed in page, one for
    ' components and one for links.
    ' This routine allows us to search for shapes without having to
    ' continually cross the com+ boundary
    '
    Public Sub initPageDictionaries(ByVal visPage As Visio.Page)


        Try

            If Not (m_dictComponents Is Nothing) Then
                m_dictComponents = Nothing
            End If
            m_dictComponents = New Dictionary(Of Integer, String)


            If Not (m_dictLinks Is Nothing) Then
                m_dictLinks = Nothing
            End If
            m_dictLinks = New Dictionary(Of Integer, String)

            Dim idShape As Integer
            Dim visShape As Visio.Shape
            Dim strTemp As String
            Dim colTemp As Collection = New Collection


            If Not (visPage Is Nothing) Then
                For Each visShape In visPage.Shapes
                    strTemp = ""
                    colTemp.Clear()
                    ' the solution will use a dghost shape to support
                    ' data graphics and get around some issues
                    If visShape.CellExists("user.dghost", False) = False Then
                        ' get the shapeID for our key in the dictionary 
                        idShape = visShape.ID
                        ' test to see if it is a cable 
                        If visShape.CellExists("prop.linkcablelabel", False) = True Then
                            If m_dictLinks.ContainsKey(idShape) = False Then
                                colTemp.Add("link")
                                colTemp.Add(visShape.Cells("prop.linkkey").ResultStr(""))
                                colTemp.Add(LCase(visShape.Name))
                                strTemp = BuildCsvString(colTemp)
                                m_dictLinks.Add(idShape, strTemp)
                            End If
                        End If
                        If visShape.CellExists("prop.complabel", False) = True Then
                            If m_dictComponents.ContainsKey(idShape) = False Then
                                colTemp.Add("comp")
                                colTemp.Add(visShape.Cells("prop.compkey").ResultStr(""))
                                colTemp.Add(LCase(visShape.Name))
                                strTemp = BuildCsvString(colTemp)
                                m_dictComponents.Add(idShape, strTemp)
                            End If
                        End If
                    End If
                Next visShape
            End If

            'Debug.Print("dictionary counts " & dictComponents.Count & " " & dictLinks.Count)

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub

    '
    ' create a csv string with end brackets from a collection
    '
    Private Function BuildCsvString _
        (ByVal colA As Collection) _
        As String

        Dim intY As Integer
        Dim strCsv As String = "[]"
        Try

            If (colA Is Nothing) _
                Or (colA.Count = 0) Then
                Exit Try
            Else
                With colA
                    ' start bracket
                    strCsv = "["
                    For intY = 1 To colA.Count
                        strCsv = strCsv & CStr(colA.Item(intY))
                        If intY <> colA.Count Then
                            strCsv = strCsv & ","
                        End If ' test for last position
                    Next intY
                    ' end bracket
                    strCsv = strCsv & "]"
                End With
            End If

        Catch ex As Exception
            '********* visio snippet vb.net visXXXX ******************
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(ex, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
            strCsv = "[]"
        End Try

        BuildCsvString = strCsv

    End Function



    '
    ' used in pagesync to get the size/zoom of the page we are leaving
    '
    Private Sub getSyncPageSize(ByVal visPage As Visio.Page)
        Try

            Dim visWindow As Visio.Window = visPage.Application.ActiveWindow
            Dim visPageSheet As Visio.Shape = visPage.PageSheet

            If visPageSheet.CellExists("prop.pagesync", False) = True Then
                Dim strPageA As String = visPage.Name
                ' This page is one of the two sides, so save the page setup
                If (LCase(strPageA) = pPageA) Or (LCase(strPageA) = pPageB) Then
                    Dim pdleft As Double
                    Dim pdtop As Double
                    Dim pdwidth As Double
                    Dim pdheight As Double
                    visWindow.GetViewRect(pdleft, pdtop, pdwidth, pdheight)
                    pLeft = pdleft
                    pTop = pdtop
                    pWidth = pdwidth
                    pHeight = pdheight
                    pZoom = visWindow.Zoom

                End If ' page sync is either pagea or pageb
            End If  ' pagesync exists

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch ex As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(ex, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Finally
        End Try

    End Sub


    '
    ' used in pagesync to set the page we are arriving
    '
    Private Sub setSyncPageSize(ByVal visPage As Visio.Page)

        Try


            ' test are we sending the correct page data

            Dim visPageSheet As Visio.Shape = visPage.PageSheet

            If visPageSheet.CellExists("prop.pagesync", False) = True Then
                ' is this the page we want to setup
                If ((pPageA = LCase(visPage.Name)) Or (pPageB = LCase(visPage.Name))) Then
                    Dim visWindow As Visio.Window = visPage.Application.ActiveWindow
                    visWindow.Zoom = pZoom
                    visWindow.SetViewRect(pLeft, pTop, pWidth, pHeight)
                End If
            End If

            initPageDictionaries(visPage)

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch ex As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(ex, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Finally
        End Try
    End Sub


    Private Function getDictionaryValue(ByVal intKey As Integer) _
        As String

        Dim retString As String = ""

        Try
            retString = m_dictComponents(intKey)
        Catch ex As Exception
        End Try

        If retString = "" Then
            Try
                retString = m_dictLinks(intKey)
            Catch ex As Exception
            End Try
        End If

        Return retString

    End Function


#End Region

#Region " query response handling "

    '// <summary>This method prompts the user to continue or cancel. If the
    '// alertResponse value is set in this Visio instance then its value 
    '// will be used and the dialog will be suppressed.</summary>
    '// <param name="eventCode">Event code of the event that fired</param>
    '// <param name="subject">Reference to subject of the event</param>
    '// <returns>False to allow the QueryCancel operation or True to cancel 
    '// the QueryCancel operation.</returns>
    Private Shared Function getQueryCancelResponse( _
        ByVal eventCode As Short, _
        ByVal subject As Object) _
        As Object

        Const docCloseCancelPrompt As String = _
            "Are you sure you want to close the document?"
        Const pageDeleteCancelPrompt As String = _
            "Are you sure you want to delete the page?"
        Const masterDeleteCancelPrompt As String = _
            "Are you sure you want to delete the master?"
        Const ungroupCancelPrompt As String = _
            "Are you sure you want to ungroup the selected shapes?"
        Const convertToGroupCancelPrompt As String = _
            "Are you sure you want to convert the selected shapes to a group?"
        Const selectionDeleteCancelPrompt As String = _
            "Are you sure you want to delete the selected shapes?"
        Const styleDeleteCancelPrompt As String = _
            "Are you sure you want to delete the style?"
        Const windowCloseCancelPrompt As String = _
            "Are you sure you want to close the window?"
        Const quitCancelPrompt As String = _
            "Are you sure you want to quit Visio?"
        Const suspendCancelPrompt As String = _
            "Are you sure you want to suspend Visio?"


        Dim subjectApplication As Microsoft.Office.Interop.Visio.Application
        Dim subjectDocument As Microsoft.Office.Interop.Visio.Document
        Dim subjectPage As Microsoft.Office.Interop.Visio.Page
        Dim subjectMaster As Microsoft.Office.Interop.Visio.Master
        Dim subjectSelection As Microsoft.Office.Interop.Visio.Selection
        Dim subjectStyle As Microsoft.Office.Interop.Visio.Style
        Dim subjectWindow As Microsoft.Office.Interop.Visio.Window

        Dim prompt As String
        Dim subjectName As String
        Dim alertResponse As Short
        Dim isQueryCancelEvent As Boolean = True
        Dim returnValue As Object = False

        Select Case (eventCode)

            ' Query Document Close
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelDocClose)

                subjectDocument = CType(subject, Microsoft.Office.Interop. _
                    Visio.Document)
                subjectName = subjectDocument.Name
                subjectApplication = subjectDocument.Application
                prompt = docCloseCancelPrompt + System.Environment. _
                NewLine + subjectName

                ' Query Cancel Page Delete
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelPageDel)

                subjectPage = CType(subject, Microsoft.Office.Interop. _
                    Visio.Page)
                subjectName = subjectPage.NameU
                subjectApplication = subjectPage.Application
                prompt = pageDeleteCancelPrompt + System.Environment. _
                    NewLine + subjectName

                ' Query Cancel Master Delete
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelMasterDel)

                subjectMaster = CType(subject, Microsoft.Office.Interop. _
                    Visio.Master)
                subjectName = subjectMaster.NameU
                subjectApplication = subjectMaster.Application
                prompt = masterDeleteCancelPrompt + System.Environment. _
                    NewLine + subjectName

                ' Query Cancel Ungroup
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelUngroup)

                subjectSelection = CType(subject, Microsoft.Office. _
                    Interop.Visio.Selection)
                subjectApplication = subjectSelection.Application
                prompt = ungroupCancelPrompt

                ' Query Cancel Convert To Group
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelConvertToGroup)

                subjectSelection = CType(subject, Microsoft.Office. _
                    Interop.Visio.Selection)
                subjectApplication = subjectSelection.Application
                prompt = convertToGroupCancelPrompt

                ' Query Cancel Selection Delete
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelSelDel)

                subjectSelection = CType(subject, Microsoft.Office. _
                    Interop.Visio.Selection)
                subjectApplication = subjectSelection.Application
                prompt = selectionDeleteCancelPrompt

                ' Query Cancel Style Delete
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelStyleDel)

                subjectStyle = CType(subject, Microsoft.Office.Interop. _
                    Visio.Style)
                subjectName = subjectStyle.NameU
                subjectApplication = subjectStyle.Application
                prompt = styleDeleteCancelPrompt + System.Environment. _
                    NewLine + subjectName

                ' Query Cancel Window Close
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelWinClose)

                subjectWindow = CType(subject, Microsoft.Office.Interop. _
                    Visio.Window)
                subjectName = subjectWindow.Caption()
                subjectApplication = subjectWindow.Application
                prompt = windowCloseCancelPrompt + System.Environment. _
                    NewLine + subjectName

                ' Query Cancel Quit
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelQuit)

                subjectApplication = CType(subject, Microsoft.Office. _
                    Interop.Visio.Application)
                prompt = quitCancelPrompt

                ' Query Cancel Suspend
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelSuspend)

                subjectApplication = CType(subject, Microsoft.Office. _
                    Interop.Visio.Application)
                prompt = suspendCancelPrompt

                ' Query Cancel Group
            Case CShort(Microsoft.Office.Interop.Visio.VisEventCodes. _
                visEvtCodeQueryCancelSelGroup)

                subjectSelection = CType(subject, Microsoft.Office. _
                    Interop.Visio.Selection)
                subjectApplication = subjectSelection.Application
                prompt = convertToGroupCancelPrompt

            Case Else
                ' This event is not cancelable.
                isQueryCancelEvent = False
                subjectApplication = Nothing
                prompt = ""
        End Select

        If (isQueryCancelEvent = True) Then

            ' check for an alertResponse setting in Visio
            If (Not subjectApplication Is Nothing) Then
                alertResponse = subjectApplication.AlertResponse
            End If

            If (alertResponse <> 0) Then

                ' if alertResponse is No or Cancel then cancel this event
                ' by returning true
                If ((alertResponse = System.Windows.Forms.DialogResult.No) _
                    Or (alertResponse = System.Windows.Forms.DialogResult. _
                    Cancel)) Then

                    returnValue = True
                End If
            Else

                ' alertResponse is not set so prompt the user
                Dim result As System.Windows.Forms.DialogResult

                result = System.Windows.Forms.MessageBox.Show(prompt, _
                    eventSinkCaption, _
                    System.Windows.Forms.MessageBoxButtons.YesNo, _
                    System.Windows.Forms.MessageBoxIcon.Question)

                If (result = System.Windows.Forms.DialogResult.No) Then
                    returnValue = True
                End If
            End If
        End If

        getQueryCancelResponse = returnValue

    End Function


#End Region

#Region " event descriptions "

    '// <summary>This method adds an event description to the
    '// eventDescriptions dictionary.</summary>
    '// <param name="eventCode">Event code of the event</param>
    '// <param name="description">Short description of the event</param>
    Private Sub addEventDescription( _
        ByVal eventCode As Short, _
        ByVal description As String)

        Dim key As String
        key = Convert.ToString(eventCode, _
            System.Globalization.CultureInfo.InvariantCulture)
        eventDescriptions.Add(key, description)
    End Sub

    '// <summary>This method returns a short description for the given
    '// eventCode.</summary>
    '// <param name="eventCode">Event code</param>
    '// <returns>Short description of the eventCode</returns>
    Private Function getEventDescription(ByVal eventCode As Short) As String
        Dim description As String
        Dim key As String

        key = Convert.ToString(eventCode, _
            System.Globalization.CultureInfo.InvariantCulture)
        description = eventDescriptions(key)

        If (description Is Nothing) Then
            description = "NoEventDescription"
        End If
        getEventDescription = description
    End Function

    '// <summary>This method populates the eventDescriptions dictionary
    '// with a short description of each Visio event code.</summary>
    Private Sub initializeStrings()

        eventDescriptions = _
            New System.Collections.Specialized.StringDictionary

        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtAfterModal), "AfterModal")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeAfterResume), "AfterResume")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtAppActivate), "AppActivated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtAppDeactivate), "AppDeactivated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtObjActivate), "AppObjActivated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtObjDeactivate), "AppObjDeactivated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDoc) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "BeforeDocumentClose")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefDocSave), "BeforeDocumentSave")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefDocSaveAs), "BeforeDocumentSaveAs")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtMaster) + CShort(Microsoft.Office.Interop. _
         Visio.VisEventCodes.visEvtDel), "BeforeMasterDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtBeforeModal), "BeforeModal")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtPage) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "BeforePageDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtBeforeQuit), "BeforeQuit")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefSelDel), "BeforeSelectionDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShape) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "BeforeShapeDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeShapeBeforeTextEdit), _
            "BeforeShapeTextEdit")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtStyle) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "BeforeStyleDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBeforeSuspend), "BeforeSuspend")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtWindow) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "BeforeWindowClose")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefWinPageTurn), "BeforeWindowPageTurn")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefWinSelDel), "BeforeWindowSelDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCell) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "CellChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtConnect) + visEvtAdd, "ConnectionsAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtConnect) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtDel), "ConnectionsDeleted")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelConvertToGroup), _
            "ConvertToGroupCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocDesign), "DesignModeEntered")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDoc) + visEvtAdd, "DocumentAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDoc) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "DocumentChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelDocClose), "DocumentCloseCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocCreate), "DocumentCreated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocOpen), "DocumentOpened")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocSave), "DocumentSaved")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocSaveAs), "DocumentSavedAs")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeEnterScope), "EnterScope")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeExitScope), "ExitScope")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtFormula) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "FormulaChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeKeyDown), "KeyDown")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeKeyPress), "KeyPress")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeKeyUp), "KeyUp")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtMaster) + visEvtAdd, "MasterAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMarker), "MarkerEvent")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtMaster) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "MasterChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelMasterDel), "MasterDeleteCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeMouseDown), "MouseDown")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeMouseMove), "MouseMove")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeMouseUp), "MouseUp")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeBefForcedFlush), _
            "MustFlushScopeBeginning")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeAfterForcedFlush), "MustFlushScopeEnded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtNonePending), "NoEventsPending")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeWinOnAddonKeyMSG), _
            "OnKeystrokeMessageForAddon")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtPage) + visEvtAdd, "PageAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtPage) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "PageChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelPageDel), "PageDeleteCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelConvertToGroup), _
            "QueryCancelConvertToGroup")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelDocClose), _
            "QueryCancelDocumentClose")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelMasterDel), _
            "QueryCancelMasterDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelPageDel), _
            "QueryCancelPageDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelQuit), "QuerCancelQuit")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelSelDel), _
            "QueryCancelSelectionDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelStyleDel), _
            "QueryCancelStyleDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelSuspend), _
            "QueryCancelSuspend")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelUngroup), _
            "QueryCancelUngroup")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelWinClose), _
            "QueryCancelWindowClose")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelQuit), "QuitCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeDocRunning), "RunModeEntered")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeSelAdded), "SelectionAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeWinSelChange), "SelectionChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelSelDel), "SelectionDeleteCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShape + visEvtAdd), "ShapeAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShape) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "ShapeChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeShapeExitTextEdit), "ShapeExitedTextEdit")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeShapeParentChange), "ShapeParentChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeShapeDelete), "ShapesDeleted")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtStyle) + visEvtAdd, "StyleAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtStyle) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "StyleChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelStyleDel), "StyleDeleteCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelSuspend), "SuspendCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtText) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "TextChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelUngroup), "UngroupCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeViewChanged), "ViewChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtIdle), "VisioIsIdle")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtApp) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtWinActivate), "WindowActivated")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelWinClose), "WindowCloseCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtWindow) + visEvtAdd, "WindowOpened")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtWindow) + CShort(Microsoft.Office.Interop. _
            Visio.VisEventCodes.visEvtMod), "WindowChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeWinPageTurn), "WindowTurnedToPage")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShapeDataGraphicChanged), "ShapeDataGraphicChanged")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShapeLinkAdded), "ShapeLinkAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtShapeLinkDeleted), "ShapeLinkDeleted")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtRemoveHiddenInformation), "RemoveHiddenInformation")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeCancelSelGroup), "GroupCanceled")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtCodeQueryCancelSelGroup), "QueryCancelGroup")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDataRecordset) + visEvtAdd, "DataRecordsetAdded")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDataRecordset) + CShort(Microsoft.Office. _
            Interop.Visio.VisEventCodes.visEvtDel), "BeforeDataRecordsetDelete")
        addEventDescription(CShort(Microsoft.Office.Interop.Visio. _
            VisEventCodes.visEvtDataRecordset) + CShort(Microsoft.Office. _
            Interop.Visio.VisEventCodes.visEvtMod), "DataRecordsetChanged")
    End Sub




#End Region


    '
    ' for debug purposes when testing scopes
    '
    Private Sub exitScopeEventHandler( _
                ByVal theApplication As Microsoft.Office.Interop.Visio.Application, _
                ByVal enterScopeID As Integer, _
                ByVal description As String, _
                ByVal errorOrCancelled As Boolean) _
                    Handles visioApplication.ExitScope

        Try

            If enterScopeID <> 1344 Then Exit Sub

            ' Verify that all incoming string parameters are not of zero 
            ' length, except for the ones that have default values as ""
            ' and the output parameters.
            If (description.Length() = 0) Then

                Throw New System.ArgumentNullException("description", _
                    "Zero length string input.")
            End If

            If theApplication.CurrentScope = enterScopeID Then

                Debug.WriteLine("Exiting my scope " & enterScopeID)

            Else

                Debug.WriteLine("Exit Scope " & description _
                    & " (" & enterScopeID & ")")
            End If

        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub



    '// <summary>This procedure searches for the "Shape Search" window. 
    '// Once we find it, we will close it.</summary>
    '// <param name="theDocument">The document in which we will 
    '// look for and close the Shape Search window</param>
    Private Sub closeShapeSearchWindow(ByVal theDocument As Document)

        Dim windowCount As Short
        Dim index As Short
        Dim currentWindow As Window
        Dim documentWindow As Window

        ' Look through the top-level windows looking for the
        ' one containing the document passed in. 
        windowCount = visioApplication.Windows.Count

        For index = 1 To windowCount
            currentWindow = visioApplication.Windows.Item(index)
            If (currentWindow.Document() Is theDocument) Then
                documentWindow = currentWindow
                closeShapeSearchWindow(documentWindow)
            End If
        Next
    End Sub

    '// <summary>This procedure closes the "Shape Search" window.
    '// </summary>
    '// <param name="activeWindow">The drawing window</param>
    Private Sub closeShapeSearchWindow(ByVal activeWindow As Window)

        Try
            If (activeWindow.Type = VisWinTypes.visDrawing) _
                And (activeWindow.SubType = VisWinTypes.visPageWin) Then
                activeWindow.Windows.ItemFromID( _
                    VisWinTypes.visWinIDShapeSearch).Visible = False
            End If
        Catch com As COMException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(com, "Com+ Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub


    ''' <summary>The GetShapeFromArguments method returns a reference to
    ''' a shape given the command line arguments.</summary>
    ''' <param name="visioApplication">The Visio application.</param>
    ''' <param name="arguments">The command line arguments string containing:
    ''' /doc=id /page=id /shape=sheet.id.</param>
    ''' <returns>The Visio shape or null.</returns>
    <CLSCompliant(False)> _
    Public Shared Function GetShapeFromArguments _
            (ByVal visioApplication As Microsoft.Office.Interop.Visio.IVApplication, _
             ByVal arguments As String) _
             As Shape

        If visioApplication Is Nothing Then
            Return Nothing
        End If

        If arguments Is Nothing Then
            Return Nothing
        End If

        Const equal As Char = "="c
        Const argumentDelimiter As Char = "/"c

        ' Standard Visio add-on command line arguments.
        Const commandLineArgumentDoc As String = "doc"
        Const commandLineArgumentPage As String = "page"
        Const commandLineArgumentShape As String = "shape"

        Dim index As Integer
        Dim docId As Integer = -1
        Dim pageId As Integer = -1
        Dim shapeId As String = ""
        Dim contextParts As String()
        Dim contextPart As String
        Dim argumentParts As String()
        Dim document As Document = Nothing
        Dim page As Page = Nothing
        Dim targetShape As Shape = Nothing

        ' Parse the command line arguments.
        contextParts = arguments.Trim().Split(argumentDelimiter)

        For index = contextParts.GetLowerBound(0) To contextParts.GetUpperBound(0)

            contextPart = contextParts(index).Trim()

            If contextPart.Length > 0 Then

                ' Separate the parameter from the parameter value.
                argumentParts = contextPart.Split(equal)

                If argumentParts.GetUpperBound(0) = 1 Then

                    ' Get the doc, page, and shape argument values.
                    If commandLineArgumentDoc.Equals(argumentParts(0)) Then

                        docId = Convert.ToInt16(argumentParts(1), System.Globalization.CultureInfo.InvariantCulture)
                    ElseIf commandLineArgumentPage.Equals(argumentParts(0)) Then

                        pageId = Convert.ToInt16(argumentParts(1), System.Globalization.CultureInfo.InvariantCulture)
                    ElseIf commandLineArgumentShape.Equals(argumentParts(0)) Then

                        shapeId = argumentParts(1)
                    End If
                End If
            End If
        Next

        ' If the command line arguments contains document, page, and shape
        ' then look up the shape.
        If (docId > 0) AndAlso (pageId > 0) AndAlso (shapeId.Length > 0) Then

            document = visioApplication.Documents(docId)
            page = document.Pages(pageId)
            targetShape = page.Shapes(shapeId)
        End If

        Return targetShape
    End Function




    Protected Overrides Sub Finalize()

        If m_dictComponents IsNot Nothing Then
            m_dictComponents = Nothing
        End If
        If m_dictLinks IsNot Nothing Then
            m_dictLinks = Nothing
        End If

        MyBase.Finalize()
    End Sub

End Class
